added Feb 2001 SDK
[windows-sources.git] / shared source / sscli20 / jscript / engine / numericunary.cs
blob8df58295a510024a92c3902ec1e28188ecece3c5
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
13 //
14 // ==--==
16 namespace Microsoft.JScript {
18 using System;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Diagnostics;
23 public sealed class NumericUnary : UnaryOp{
24 private Object metaData = null;
25 private JSToken operatorTok;
26 private MethodInfo operatorMeth;
27 private Type type;
29 internal NumericUnary(Context context, AST operand, JSToken operatorTok)
30 : base(context, operand){
31 this.operatorTok = operatorTok;
32 this.operatorMeth = null;
33 this.type = null;
36 public NumericUnary(int operatorTok)
37 : this(null, null, (JSToken)operatorTok){
40 internal override Object Evaluate(){
41 return this.EvaluateUnary(this.operand.Evaluate());
44 #if !DEBUG
45 [DebuggerStepThroughAttribute]
46 [DebuggerHiddenAttribute]
47 #endif
48 public Object EvaluateUnary(Object v){
49 IConvertible ic = Convert.GetIConvertible(v);
50 switch(Convert.GetTypeCode(v, ic)){
51 case TypeCode.Empty: return this.EvaluateUnary(Double.NaN);
52 case TypeCode.DBNull: return this.EvaluateUnary(0);
53 case TypeCode.Boolean: return this.EvaluateUnary(ic.ToBoolean(null) ? 1 : 0);
54 case TypeCode.Char: return this.EvaluateUnary((int)ic.ToChar(null));
56 case TypeCode.SByte:
57 case TypeCode.Byte:
58 case TypeCode.Int16:
59 case TypeCode.UInt16:
60 case TypeCode.Int32:
61 int i = ic.ToInt32(null);
62 switch (this.operatorTok){
63 case JSToken.BitwiseNot:
64 return ~i;
65 case JSToken.LogicalNot:
66 return i == 0;
67 case JSToken.Minus:
68 if (i == 0) return -(double)i;
69 if (i == Int32.MinValue) return (ulong)-(double)i;
70 return -i;
71 case JSToken.Plus:
72 return i;
73 default:
74 throw new JScriptException(JSError.InternalError, this.context);
77 case TypeCode.UInt32:
78 uint ui = ic.ToUInt32(null);
79 switch (this.operatorTok){
80 case JSToken.BitwiseNot:
81 return ~ui;
82 case JSToken.LogicalNot:
83 return ui == 0;
84 case JSToken.Minus:
85 if (ui != 0 && ui <= Int32.MaxValue)
86 return -(int)ui;
87 else
88 return -(double)ui;
89 case JSToken.Plus:
90 return ui;
91 default:
92 throw new JScriptException(JSError.InternalError, this.context);
95 case TypeCode.Int64:
96 long l = ic.ToInt64(null);
97 switch (this.operatorTok){
98 case JSToken.BitwiseNot:
99 return ~l;
100 case JSToken.LogicalNot:
101 return l == 0;
102 case JSToken.Minus:
103 if (l == 0 || l == Int64.MinValue) return -(double)l;
104 return -l;
105 case JSToken.Plus:
106 return l;
107 default:
108 throw new JScriptException(JSError.InternalError, this.context);
111 case TypeCode.UInt64:
112 ulong ul = ic.ToUInt64(null);
113 switch (this.operatorTok){
114 case JSToken.BitwiseNot:
115 return ~ul;
116 case JSToken.LogicalNot:
117 return ul == 0;
118 case JSToken.Minus:
119 if (ul != 0 && ul <= Int64.MaxValue)
120 return -(long)ul;
121 else
122 return -(double)ul;
123 case JSToken.Plus:
124 return ul;
125 default:
126 throw new JScriptException(JSError.InternalError, this.context);
129 case TypeCode.Single:
130 case TypeCode.Double:
131 double d = ic.ToDouble(null);
132 switch (this.operatorTok){
133 case JSToken.BitwiseNot:
134 return ~(int)Runtime.DoubleToInt64(d);
135 case JSToken.LogicalNot:
136 return !Convert.ToBoolean(d);
137 case JSToken.Minus:
138 return -d;
139 case JSToken.Plus:
140 return d;
141 default:
142 throw new JScriptException(JSError.InternalError, this.context);
145 case TypeCode.String:
146 goto no_overload_case;
149 MethodInfo oper = this.GetOperator(v.GetType());
150 if (oper != null)
151 return oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v}, null);
152 no_overload_case:
153 switch (this.operatorTok){
154 case JSToken.BitwiseNot:
155 return ~Convert.ToInt32(v, ic);
156 case JSToken.LogicalNot:
157 return !Convert.ToBoolean(v, ic);
158 case JSToken.Minus:
159 return -Convert.ToNumber(v, ic);
160 case JSToken.Plus:
161 return Convert.ToNumber(v, ic);
162 default:
163 throw new JScriptException(JSError.InternalError, this.context);
167 private MethodInfo GetOperator(IReflect ir){
168 Type t = ir is Type ? (Type)ir : Typeob.Object;
169 if (this.type == t)
170 return this.operatorMeth;
171 this.type = t;
172 if (Convert.IsPrimitiveNumericType(t) || Typeob.JSObject.IsAssignableFrom(t)){
173 this.operatorMeth = null;
174 return null;
176 switch (this.operatorTok){
177 case JSToken.BitwiseNot:
178 this.operatorMeth = t.GetMethod("op_OnesComplement", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
179 case JSToken.LogicalNot:
180 this.operatorMeth = t.GetMethod("op_LogicalNot", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
181 case JSToken.Minus:
182 this.operatorMeth = t.GetMethod("op_UnaryNegation", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
183 case JSToken.Plus:
184 this.operatorMeth = t.GetMethod("op_UnaryPlus", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
185 default:
186 throw new JScriptException(JSError.InternalError, this.context);
188 if (this.operatorMeth == null ||
189 (operatorMeth.Attributes & MethodAttributes.SpecialName) == 0 ||
190 operatorMeth.GetParameters().Length != 1)
191 this.operatorMeth = null;
192 if (this.operatorMeth != null)
193 this.operatorMeth = new JSMethodInfo(this.operatorMeth);
194 return this.operatorMeth;
197 internal override IReflect InferType(JSField inference_target){
198 Debug.Assert(Globals.TypeRefs.InReferenceContext(this.type));
199 MethodInfo oper;
200 if (this.type == null || inference_target != null){
201 oper = this.GetOperator(this.operand.InferType(inference_target));
202 }else
203 oper = this.GetOperator(this.type);
204 if (oper != null){
205 this.metaData = oper;
206 return oper.ReturnType;
208 if (this.operatorTok == JSToken.LogicalNot) return Typeob.Boolean;
209 switch (Type.GetTypeCode(this.type)){
210 case TypeCode.Empty: return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
211 case TypeCode.Object: return Typeob.Object;
212 case TypeCode.DBNull: return Typeob.Int32;
213 case TypeCode.Boolean: return Typeob.Int32;
214 case TypeCode.SByte: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.SByte : Typeob.Int32;
215 case TypeCode.Char: return Typeob.Int32;
216 case TypeCode.Byte: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Byte : Typeob.Int32;
217 case TypeCode.Int16: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Int16 : Typeob.Int32;
218 case TypeCode.UInt16: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.UInt16 : Typeob.Int32;
219 case TypeCode.Int32: return Typeob.Int32;
220 case TypeCode.UInt32: return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt32;
221 case TypeCode.Int64: return Typeob.Int64;
222 case TypeCode.UInt64: return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt64;
223 case TypeCode.Single:
224 case TypeCode.Double:
225 case TypeCode.String: return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
227 if (Typeob.JSObject.IsAssignableFrom(this.type))
228 return Typeob.Double;
229 else
230 return Typeob.Object;
233 internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm){
234 if (this.operatorTok == JSToken.LogicalNot)
235 this.operand.TranslateToConditionalBranch(il, !branchIfTrue, label, shortForm);
236 else
237 base.TranslateToConditionalBranch(il, branchIfTrue, label, shortForm);
240 internal override void TranslateToIL(ILGenerator il, Type rtype){
241 if (this.metaData == null){
242 Type rt = this.operatorTok == JSToken.LogicalNot ? Typeob.Boolean : Typeob.Double;
243 if (Convert.IsPrimitiveNumericType(rtype) && Convert.IsPromotableTo(this.type, rtype))
244 rt = rtype;
245 if (this.operatorTok == JSToken.BitwiseNot && !Convert.IsPrimitiveIntegerType(rt)){
246 rt = this.type;
247 if (!Convert.IsPrimitiveIntegerType(rt))
248 rt = Typeob.Int32;
250 this.operand.TranslateToIL(il, this.type);
251 Convert.Emit(this, il, this.type, rt, true);
252 switch (this.operatorTok){
253 case JSToken.BitwiseNot:
254 il.Emit(OpCodes.Not);
255 break;
256 case JSToken.LogicalNot:
257 Convert.Emit(this, il, rt, Typeob.Boolean, true);
258 rt = Typeob.Boolean;
259 il.Emit(OpCodes.Ldc_I4_0);
260 il.Emit(OpCodes.Ceq);
261 break;
262 case JSToken.Minus:
263 il.Emit(OpCodes.Neg);
264 break;
265 case JSToken.Plus:
266 break;
267 default:
268 throw new JScriptException(JSError.InternalError, this.context);
270 Convert.Emit(this, il, rt, rtype);
271 return;
273 if (this.metaData is MethodInfo){
274 MethodInfo oper = (MethodInfo)this.metaData;
275 ParameterInfo[] pars = oper.GetParameters();
276 this.operand.TranslateToIL(il, pars[0].ParameterType);
277 il.Emit(OpCodes.Call, oper);
278 Convert.Emit(this, il, oper.ReturnType, rtype);
279 return;
281 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
282 //Compile operands to objects and devolve the decision making to run time thunks
283 il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
284 this.operand.TranslateToIL(il, Typeob.Object);
285 il.Emit(OpCodes.Call, CompilerGlobals.evaluateUnaryMethod);
286 Convert.Emit(this, il, Typeob.Object, rtype);
289 internal override void TranslateToILInitializer(ILGenerator il){
290 IReflect rtype = this.InferType(null);
291 this.operand.TranslateToILInitializer(il);
292 if (rtype != Typeob.Object)
293 return;
294 this.metaData = il.DeclareLocal(Typeob.NumericUnary);
295 ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
296 il.Emit(OpCodes.Newobj, CompilerGlobals.numericUnaryConstructor);
297 il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);